/*
 * Copyright (c) 2012, Markus Weber All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without modification, are
 * permitted provided that the following conditions are met:
 * 
 * Redistributions of source code must retain the above copyright notice, this list of
 * conditions and the following disclaimer. Redistributions in binary form must reproduce the
 * above copyright notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 * 
 * Neither the name of the author nor the names of its contributors may be used to endorse or
 * promote products derived from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * 
 */
package de.dfki.graph.db;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.SortedSet;

import org.apache.log4j.Logger;

import de.dfki.graph.EmptyQueryException;
import de.dfki.graph.LabeledGraph;
import de.dfki.graph.index.SearchResult;
import de.dfki.graph.subgraphmatching.IndexedSubGraphMatcher;
import de.dfki.graph.subgraphmatching.MessmerBunkeSubgraphMatcher;

/**
 * <code>GraphDatabase</code>
 * 
 * @author Markus Weber
 * 
 */
public class GraphDatabase {
    /** Logger */
    private Logger log = Logger.getLogger(GraphDatabase.class);

    /** Reference to database folder. */
    public static File DBFOLDER = new File("database");

    /** Singleton instance. */
    private static GraphDatabase instance;

    /** Mapping : name of database index. */
    private Map<String, IndexedSubGraphMatcher> database;

    /** List with todos */
    private Map<String, List<LabeledGraph>> todos;

    /** List of file writers */
    private Map<String, FileWriter> logging;

    /**
     * Constructor
     */
    private GraphDatabase() {
	database = new HashMap<String, IndexedSubGraphMatcher>();
	todos = new HashMap<String, List<LabeledGraph>>();
	logging = new HashMap<String, FileWriter>();
	initDatabase();
    }

    /**
     * Initializes the database.
     */
    private void initDatabase() {
	if (!DBFOLDER.exists()) {
	    log.info("Generate new database");
	    DBFOLDER.mkdirs();
	}
    }

    /**
     * Returns an instance of the graph database.
     * 
     * @return
     */
    public static GraphDatabase instance() {
	if (instance == null) {
	    instance = new GraphDatabase();
	}
	return instance;
    }

    /**
     * Queries a defined database.
     * 
     * @param dbname
     * @param query
     * @return
     * @throws EmptyQueryException
     */
    public SortedSet<SearchResult> queryDatabase(String dbname,
	    LabeledGraph query) throws EmptyQueryException {
	log.debug("[Database] : " + dbname + " [query] : " + query);
	return this.database.get(dbname).retrieve(query, 5);
    }

    /**
     * Adding graph to database.
     * 
     * @param name
     * @param graph
     */
    public void addLabeledGraph(String name, LabeledGraph graph) {
	if (!this.todos.containsKey(name)) {
	    log.debug("Create new todo list for [db] : " + name);
	    this.todos.put(name, new LinkedList<LabeledGraph>());
	}
	log.debug("Add new graph [db] : " + name + " graph : "
		+ graph.getName());
	this.todos.get(name).add(graph);
    }

    /**
     * Returns the number of unindexed graphs.
     * 
     * @param db
     * @return
     */
    public int getNumberOfUnindexedGraphs(String db) {
	return this.todos.containsKey(db) ? this.todos.get(db).size() : 0;
    }

    /**
     * Build index.
     * 
     * @param name
     */
    public void buildIndex(String name) {
	if (!this.database.containsKey(name)) {
	    this.database.put(name, new MessmerBunkeSubgraphMatcher(
		    new HashMap<String, Byte>()));
	    File dbinstanceFolder = new File(DBFOLDER.getAbsolutePath()
		    + File.separator + name);
	    if (!dbinstanceFolder.exists()) {
		dbinstanceFolder.mkdirs();
	    }
	    try {
		logging.put(
			name,
			new FileWriter(new File(dbinstanceFolder
				.getAbsolutePath()
				+ File.separator
				+ "result.csv")));
	    } catch (IOException e) {
		log.error(e);
	    }
	    log.debug("Create new index for [db] : " + name);
	}
	IndexedSubGraphMatcher matcher = this.database.get(name);
	if (this.todos.containsKey(name)) {
	    Writer debug = this.logging.get(name);
	    matcher.buildIndex(this.todos.get(name), debug);
	    try {
		debug.close();
	    } catch (IOException e) {
		log.error(e);
	    }
	}
    }

    /**
     * 
     * @param name
     */
    public void simulateIndexBuilding(String name) {
	if (!this.database.containsKey(name)) {
	    this.database.put(name, new MessmerBunkeSubgraphMatcher(
		    new HashMap<String, Byte>()));
	    File dbinstanceFolder = new File(DBFOLDER.getAbsolutePath()
		    + File.separator + name);
	    if (!dbinstanceFolder.exists()) {
		dbinstanceFolder.mkdirs();
	    }
	    try {
		logging.put(
			name,
			new FileWriter(new File(dbinstanceFolder
				.getAbsolutePath()
				+ File.separator
				+ "result.csv")));
	    } catch (IOException e) {
		log.error(e);
	    }
	    log.debug("Create new index for [db] : " + name);
	}
	IndexedSubGraphMatcher matcher = this.database.get(name);
	if (this.todos.containsKey(name)) {
	    Writer debug;
	    try {
		debug = new FileWriter(DBFOLDER.getAbsolutePath()
			+ File.separator + name + File.separator
			+ "simulation.csv");

		matcher.simulateIndex(this.todos.get(name), debug);

		debug.close();
	    } catch (IOException e) {
		log.error(e);
	    }

	}
    }
}
